import { observer }  from './observer/index';
import { nextTick } from './utils/nextTick';
import Watcher from './observer/wacher';
import Dep from './observer/dep';

export function initState(vm){
  let opts = vm.$options;
  if (opts.data) {
    initData(vm)
  }
  if (opts.methods) {
    initMethods(vm)
  }
  if (opts.props) {
    initProps(vm)
  }
  if (opts.computed) {
    initComputed(vm)
  }
  if (opts.watch) {
    initWatch(vm)
  }
  if (opts.mixins) {
    initMixins(vm)
  }
}

function initData(vm){
  let data;
  if (typeof vm.$options.data === 'function') {
    data = vm.$options.data.call(vm);
  } else {
    data = vm.$options.data;
  }
  vm._data = data;
  for (let key in data) {
    proxy(vm, '_data', key)
  }
  observer(data);
}

function proxy(vm, source, key){
  Object.defineProperty(vm, key, {
    get(){
      return vm[source][key]
    },
    set(newValue){
      vm[source][key] = newValue;
    }
  })
}
function initMethods(){

}
function initProps(){

}
function initComputed(vm){
  let computed = vm.$options.computed
  let watcher = vm._computedWatchers = {};
  for(let key in computed) {
    // 三种方式
    let userDef = computed[key];
    let getter = typeof userDef === 'function' ? userDef : userDef.get;
    watcher[key] = new Watcher(vm, getter.bind(vm), ()=>{}, {lazy: true})
    defineComputed(vm, key, userDef)
  }
}
let sharedPropDefinition= {};
function defineComputed(target, key, userDef){
  sharedPropDefinition = {
    enumerable: true,
    configurable: true,
    get(){},
    set(){}
  }
  // 添加缓存机制
  sharedPropDefinition.get = createComputedGetter(key)
  if (typeof userDef !== 'function') {
    sharedPropDefinition.set = userDef.set.bind(vm)
  }
  Object.defineProperty(target, key, sharedPropDefinition)
}

function createComputedGetter(key){
  return function(){
    let watcher = this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate() // 走用户的方法
      }
      if (Dep.target) {
        // 收集渲染watcher
        watcher.depend()
      }
    }
    return watcher.value
  }
}
function initWatch(vm){
  let watch = vm.$options.watch;
  for (let key in watch) {
    // 获取属性对应的值
    let handler = watch[key]; // 数组， 对象， 函数， 字符
    if (Array.isArray(handler)) {
      handler.forEach(item => {
        createWatcher(vm, key, item);
      })
    } else {
      createWatcher(vm, key, handler);
    }
  }
}
function initMixins(){

}

export function stateMixin(Vue){
  // 列队  vue自己的nextTick + 用户自己的
  Vue.prototype.$nextTick = function(cb){
    // 数据更新后执行
    nextTick(cb);
  }
  Vue.prototype.$watch = function (expOrFn, handler, options){
    let watcher = new Watcher(this, expOrFn, handler,{...options, user: true})
    if (options?.immediate) {
      handler.call(this);
    }
  }
}

function createWatcher(vm, expOrFn, handler, options){
  if (typeof handler ==='object') {
    options = handler;
    handler = handler.handler; // 用户的配置项
  }
  if (typeof handler === 'string'){
    handler = vm[handler] // 将实例上的方法作为handler
  }
  // watch的最终处理 $watch
  return vm.$watch(expOrFn, handler, options);
}